package plus;

import java.util.*;

import plus.decl.D_Import;
import plus.exp.E_Call;
import plus.run.Loader;
import plus.util.Cons;
/**
 * Perhaps this should be called "compilation context".
 * The name was from "unification" which was the original intent: a home for
 * data and certain algorithms related to the overall type-unification of an
 * independent unit of compilation.
 * 
 * Anyway, it now handles both type unification and type-dependent function
 * name binding.
 * 
 * @author me
 *
 */
public class Uni {
	public static boolean progress;
	public static LinkedList<Constraint> constraints;
	public static LinkedList<E_Call> sites;
	
	static Cons<FEnv> fes = null;
	public static void pushFE(FEnv fe) { fes = new Cons<FEnv>(fe, fes); }
	public static void popFE() { fes = fes.tail; }
	
	static Cons<Module> mods = null;
	static Map<String, Module> modMap = null;
	public static void findFunction(String name, int arity, Collection<Symbol<Function>> candidates) {
		// Look in the nest of scopes
		for (Cons<FEnv> c = fes; c != null; c = c.tail) {
			c.head.find_function(name, arity, candidates);
		}
		// Take a glance at such modules as are currently "active".
		for (Cons<Module> m = mods; m != null; m = m.tail) {
			m.head.fe.find_function(name, arity, candidates);
		}
	}
	public static Module need_module(String scope, int lineNr) {
		Module mod = modMap.get(scope);
		if (mod == null) throw new Gripe(lineNr+": No such module "+scope);
		else return mod;
	}
	public static boolean converge() {
		boolean solved;
		do {
			progress = false;
			solved = true;
			for (E_Call cs:sites) {
				if (cs.solve()) cs.runConformance();
				else solved = false;
			}
			for (Constraint c:constraints) {
				if (c.solve()) {
					constraints.remove(c);
				}
				else solved = false;
			}
		} while (progress);
		return solved;
	}
	public static void reportConvergenceFailure() {
		System.err.println("Remaining Call Sites:");
		for (E_Call cs:sites) if (!cs.isSolved()) cs.report();
		System.err.println("Remaining Constraints:");
		for (Constraint c:constraints) System.err.println("\t"+c);
	}
	public static void prepare(List<D_Import> imports) {
		constraints = new LinkedList<Constraint>();
		sites = new LinkedList<E_Call>();
		mods = null; // Searched for unqualified identifiers
		modMap = new HashMap<String, Module>(); // Searched for qualified identifiers
		installAnonymousModule(Loader.builtin);
		for (D_Import i: imports) i.install();
		
	}
	public static void installAnonymousModule(Module module) {
		mods = new Cons<Module>(module, mods);
	}
	public static void installNamedModule(String name, Module module) {
		if (modMap.containsKey(name)) throw new Gripe("Please don't use the same name ("+name+") for two modules.");
		modMap.put(name, module);
	}
	public static Symbol<TypeSym> findType(String name) {
		// First look in the present stack of type environments.
		Symbol<TypeSym> it = TEnv.findType(name);
		if (null != it) return it;
		
		// If that didn't work, check through the list of imports.
		// By this logic, we take the the most recently-installed
		// type from an anonymous module.
		for (Cons<Module> m = mods; m != null; m = m.tail) {
			it = m.head.types.find(name);
			if (null != it) return it;
		}
		return null;
	}
}
